from PySide6.QtCore import QEvent
from PySide6.QtGui import QPainter, Qt, QMouseEvent, QDragEnterEvent, QDragMoveEvent, QDropEvent
from PySide6.QtWidgets import QGraphicsView

from 节点插槽 import NodePort, PORT_TYPE_OUT
from 节点连接线 import DraggingEdge


class CanvasBase(QGraphicsView):
    def __init__(self, sw=None):
        super().__init__()

        self.sw = sw
        self.factor = None
        self.setViewportUpdateMode(QGraphicsView.ViewportUpdateMode.FullViewportUpdate)
        self.setRenderHints(  # 设置渲染提示, 使图形更加平滑, 但会消耗更多的资源
            QPainter.RenderHint.Antialiasing | QPainter.RenderHint.TextAntialiasing
            | QPainter.RenderHint.SmoothPixmapTransform | QPainter.RenderHint.LosslessImageRendering
        )
        self.setAcceptDrops(True)  # 支持拖拽
        self.setDragMode(QGraphicsView.DragMode.RubberBandDrag)
        self.setMouseTracking(True)  # 启用鼠标跟踪以接收鼠标移动事件，即使没有按下按钮

        # 隐藏滚动条
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
        self.setRubberBandSelectionMode(Qt.ItemSelectionMode.IntersectsItemShape)  # 橡皮筋拖动

        self._zoom_clamp = [0.1, 2]
        self._zoom_factor = 1.1
        self._view_scale = 1.0
        self._last_scale = None
        self.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)  # 缩放围绕鼠标为中心
        self._last_mouse_pos = None  # 记录鼠标位置

        # 画布拖动
        self._drag_mode = False

        # 拖动的边
        self._dragging_edge = None
        self._dragging_edge_mode = False

    def wheelEvent(self, event):  # 鼠标滚轮事件, 缩放画布
        if not self._drag_mode:
            zoom_factor = self._zoom_factor if event.angleDelta().y() > 0 else 1 / self._zoom_factor
            self._view_scale *= zoom_factor
            if not (self._zoom_clamp[0] <= self._view_scale <= self._zoom_clamp[1]):
                zoom_factor = 1.0
                self._view_scale = self._last_scale
            self._last_scale = self._view_scale
            self.scale(zoom_factor, zoom_factor)

    def mouseDoubleClickEvent(self, event):  # 鼠标双击事件
        if event.button() == Qt.MouseButton.RightButton:
            self.resetTransform()  # 重置缩放
            self._view_scale = 1.0
        else:
            super().mouseDoubleClickEvent(event)

    # 以下为拖放相关的事件处理
    def dragEnterEvent(self, event: QDragEnterEvent):
        if event.mimeData().hasFormat("application/x-qabstractitemmodeldatalist"):
            event.acceptProposedAction()

    def dragMoveEvent(self, event: QDragMoveEvent):
        if event.mimeData().hasFormat("application/x-qabstractitemmodeldatalist"):
            event.acceptProposedAction()

    def dropEvent(self, event: QDropEvent):
        if event.mimeData().hasFormat("application/x-qabstractitemmodeldatalist"):
            # 获取拖放位置
            pos = event.position().toPoint()
            scene_pos = self.mapToScene(pos)
            # 在此处添加自定义的拖放处理逻辑
            self.sw.open_test(scene_pos)

    # 以下为鼠标点击的事件处理
    def mousePressEvent(self, event):
        if event.button() == Qt.MouseButton.RightButton:
            self.right_mouse_press(event)  # 右键按下事件
        elif event.button() == Qt.MouseButton.LeftButton:
            self.left_mouse_press(event)  # 左键按下事件
        return super().mousePressEvent(event)

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.MouseButton.RightButton:
            self.right_mouse_release(event)  # 右键松开事件
        elif event.button() == Qt.MouseButton.LeftButton:
            self.left_mouse_release(event)  # 左键松开事件
        return super().mouseReleaseEvent(event)

    # 以下为右键点击的事件处理, 用于拖动画布的处理
    def right_mouse_press(self, event):  # 右键按下事件
        if self.itemAt(event.pos()) is not None:  # 如果鼠标位置有图形项
            return
        else:
            # 创建虚拟的左键松开事件
            release_event = QMouseEvent(
                QEvent.Type.MouseButtonRelease, event.pos(),
                Qt.MouseButton.LeftButton, Qt.MouseButton.NoButton,
                event.modifiers()
            )
            super().mouseReleaseEvent(release_event)
            # 设置拖动模式为滚动手柄拖动，因为右键按下时只会切换到拖动模式，
            # 所以还需要使用左键进行拖动，为了方便，这里直接虚拟了一个左键点击事件进行拖动
            self.setDragMode(QGraphicsView.DragMode.ScrollHandDrag)
            self._drag_mode = True
            # 创建虚拟的左键点击事件
            click_event = QMouseEvent(
                QEvent.Type.MouseButtonPress, event.pos(),
                Qt.MouseButton.LeftButton, Qt.MouseButton.NoButton,
                event.modifiers()
            )
            super().mousePressEvent(click_event)

    def right_mouse_release(self, event):
        if self._drag_mode:
            release_event = QMouseEvent(
                QEvent.Type.MouseButtonRelease, event.pos(),
                Qt.MouseButton.LeftButton, Qt.MouseButton.NoButton,
                event.modifiers()
            )
            super().mouseReleaseEvent(release_event)
            # 设置拖动模式为橡皮筋拖动
            self.setDragMode(QGraphicsView.DragMode.RubberBandDrag)
            self._drag_mode = False
        else:
            super().mouseReleaseEvent(event)

    def left_mouse_press(self, event: QMouseEvent):  # 左键按下事件
        """左键按下事件，用于处理节点连接线的拖动添加"""
        mouse_pos = event.pos()
        item = self.itemAt(mouse_pos)  # 获取鼠标位置的图形项
        if isinstance(item, NodePort):  # 如果图形项是节点插槽
            self._dragging_edge_mode = True  # 设置拖动边模式为True
            self.create_dragging_edge(item)  # 创建拖动的边
        else:
            self._dragging_edge_mode = False  # 确保在不拖动节点连接线时关闭拖动模式
            super().mousePressEvent(event)

    # 以下为拖动的边的处理
    def create_dragging_edge(self, port: NodePort):
        """创建拖动的边"""
        port_pos = port.get_port_pos()  # 获取插槽的位置
        port_color = port.get_port_color()  # 获取插槽的颜色
        drag_from_source = port.port_type == PORT_TYPE_OUT  # 判断是否是从源插槽拖动
        if self._dragging_edge is None:
            self._dragging_edge = DraggingEdge(port_pos, port_pos, port_color, drag_from_source, self.scene())
            self._dragging_edge.update_edge_path()  # 更新边的路径
            self.scene().addItem(self._dragging_edge)  # 添加到场景中

    def mouseMoveEvent(self, event: QMouseEvent):  # 鼠标移动事件
        if self._dragging_edge_mode:
            self._dragging_edge.update_positions(self.mapToScene(event.pos()))  # 更新拖动的边的位置
        else:
            super().mouseMoveEvent(event)

    def left_mouse_release(self, event: QMouseEvent):
        if self._dragging_edge_mode:
            self._dragging_edge_mode = False  # 关闭拖动边模式
            item = self.itemAt(event.pos())
            if isinstance(item, NodePort):
                # self._dragging_edge.connect_ports(item)
                pass
            # 删除拖动的边
            self.scene().removeItem(self._dragging_edge)
            self._dragging_edge = None
        super().mouseReleaseEvent(event)
